package edu.odu.cs.sstore.domain;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;

import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import edu.odu.cs.sstore.dto.UserDTO;
 
@Entity
public class User implements UserDetails,Serializable {
	
	private static final long serialVersionUID = 7186308094679499354L;

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;
	
	@Column(unique=true, nullable=false) 
	private String username;
	private String password;

	@Column(nullable=false) 
	private String name;
	
	@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "userId")
    private Set<Authority> authorities;
	
	@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	private Set<Course> registeredCourses;
	
	@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	private Set<Document> documents;

	public User() {
		this.authorities = new HashSet<Authority>();
		this.registeredCourses = new HashSet<Course>();
		this.documents = new HashSet<Document>();
		this.username = null;
		this.name = null;
	}
 
	public Long getId() {
		return this.id;
	}
 
	public void setId(Long id) {
		this.id = id;
	}

	public void setPassword(String password) {
		PasswordEncoder encoder = new ShaPasswordEncoder();
		this.password = encoder.encodePassword(password, null);
	}

	public String getPassword() {
		return this.password;
	}
	
	public boolean validatePassword(String password) {
		PasswordEncoder encoder = new ShaPasswordEncoder();
		return (this.password == encoder.encodePassword(password, null));
	}

	public String getUsername() {
		return this.username;
	}
 
	public void setUsername(String username) {
		this.username = username;
	}
	
	public String getName() {
		return this.name;
	}
 
	public void setName(String name) {
		this.name = name;
	}

	public Collection<GrantedAuthority> getAuthorities() {
		HashSet<GrantedAuthority> h = new HashSet<GrantedAuthority>();
        for (GrantedAuthority auth : authorities) {
            h.add(auth);
        }
        return h;
	}
	
	public boolean hasAuthority(Authority authority) {
		return this.authorities.contains(authority);
	}
	
	public boolean hasAuthority(String authority) {
		for(Authority a : this.authorities)
			if(a.getAuthority().equals(authority))
				return true;
		return false;		
	}
	
	public void addAuthority(Authority authority) {
		this.authorities.add(authority);
	}

	public void removeAuthority(Authority authority) {
		this.authorities.remove(authority);
	}
	
	public Set<Course> getRegisteredCourses() {
		return registeredCourses;
	}

	public void addRegisteredCourse(Course course) {
		this.registeredCourses.add(course);
	}
	
	public void removeRegisteredCourse(Course course) {
		this.registeredCourses.remove(course);
	}
	
	public void addDocument(Document document) {
		this.documents.add(document);
	}
	
	public void removeDocument(Document document) {
		this.documents.remove(document);
	}
	
	public Set<Document> getDocuments() {
		return this.documents;
	}
	
	/**
	 * This method is a requirement of the UserDetails interface.  Although required
	 * by the interface, this method has no functionality in this application.
	 */
	public boolean isAccountNonExpired() {
        return true;
    }
	
	/**
	 * This method is a requirement of the UserDetails interface.  Although required
	 * by the interface, this method has no functionality in this application.
	 */
    public boolean isAccountNonLocked() {
        return true;
    }
    
    /**
	 * This method is a requirement of the UserDetails interface.  Although required
	 * by the interface, this method has no functionality in this application.
	 */
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
	 * This method is a requirement of the UserDetails interface.  Although required
	 * by the interface, this method has no functionality in this application.
	 */
	public boolean isEnabled() {
		return true;
	}

	public UserDTO toDTO() {
		// Convert roles into strings
		Set<String> authorities = new HashSet<String>();
		for(Authority authority : this.authorities)
			authorities.add(authority.getAuthority());
		
		return new UserDTO(
			this.id,
			this.username,
			this.name,
			authorities
		);
	}
	
	public String toString() {
		return this.username.toString();
	}
}
